// +build shanghai

package game_frame

import (
	"context"
	"errors"
	"fmt"
	"gamesdk/common/log"
	"gamesdk/common/recover"
	"gamesdk/global"
	"gamesdk/inter"
	"gamesdk/msg"
	"gamesdk/registry"
	"net"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/golang/protobuf/proto"
	uuid "github.com/satori/go.uuid"
)

type AcceptMsg struct {
	client       *Client
	s2cNoticeMsg *msg.S2CNoticeMsg
}
type Room struct {
	token         string
	userManager   *UserManager
	tableManager  *TableManager
	aiManager     *AIManager
	interRoom     inter.RoomInter
	robotCtl      sync.Map //机器人控制
	marqueeConfig sync.Map //跑马灯
	close         chan bool
	acceptClose   chan bool
	//client        msg.RoomClient
	conns         sync.Map //链接管理
	acceptMsg     chan *AcceptMsg
	waitGroup     sync.WaitGroup
	waitUserGroup sync.WaitGroup
	match         *Match
	ctx           context.Context
	msgLog        sync.Map //消费时间存储
	openRoom      sync.Map //消费时间存储
	lock          sync.Mutex
	conn          net.Conn
	exit          bool
}

//初始化房间
func NewRoom(interRoom inter.RoomInter) *Room {
	log.Info("初始化桌子")
	room := &Room{
		interRoom:   interRoom,
		close:       make(chan bool),
		acceptClose: make(chan bool),
		ctx:         context.Background(),
		acceptMsg:   make(chan *AcceptMsg, global.G_Config.Max*global.G_Config.TableNum),
		conn:        nil,
		exit:        false,
	}

	//建立链接
	token := room.funcGetToken()
	room.token = token
	room.userManager = &UserManager{waitGroup: &room.waitUserGroup}
	room.tableManager = NewTableManager(global.G_Config.TableNum, global.G_Config.Max, global.G_Config.Min, room, room.interRoom)
	//初始化机器人
	room.aiManager = NewAIManager(room)
	room.userManager.GetOnlineAndOfflineNum()
	return room
}

func (this *Room) funcGetToken() string {
	defer log.Trace()()
	// f, err := os.OpenFile("token.db", os.O_RDWR|os.O_CREATE, 0600)
	// defer f.Close()
	// if err != nil {
	// 	panic(err.Error())
	// } else {
	// 	buf := bufio.NewReader(f)
	// 	line, err := buf.ReadString('\n')
	// 	if err != nil {
	// 		if len(line) <= 0 {
	token, err := uuid.NewV4()
	if err != nil {
		panic(err.Error())
	}
	t := fmt.Sprintf("%s", token)
	// bufReader := bufio.NewWriter(f)
	// bufReader.WriteString(t)
	// bufReader.Flush()

	return t
	// 		}
	// 	}
	// 	line = strings.TrimSpace(line)
	// 	return string(line)
	// }
}

//启动客户端
func (this *Room) Run() {
	log.Info("服务启动")
	//监听退出消息
	c := make(chan os.Signal)
	signal.Notify(c, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGQUIT)
	go func() {
		this.waitGroup.Add(1)
		defer this.waitGroup.Done()
		this.match = NewMatch(this)
		this.funcAccept()
		this.match.Exit()
	}()
	ticker := time.NewTicker(time.Second * 10)
	go func() {
		for {
			select {
			case <-ticker.C:
				log.Info("刷新地址")
				if this.exit {
					return
				}
				if list, e := registry.ServiceGateways(global.G_Config.GameId); e == nil {
					log.Info("list%v", list)
					for _, server := range *list {
						//ip, port,
						grpc_url := fmt.Sprintf("%s:%d", server.Address, server.Port)
						tcp_url := fmt.Sprintf("%s:%d", server.Address, server.Port+2)
						//log.Info("server ID %v", server)
						_, ok := this.conns.Load(server.ID)
						if !ok {
							conn := NewClient(tcp_url, grpc_url, server.ID, 10, this)
							if conn != nil {

							} else {
								log.Error("链接失败 grpc:%s tcp:%s", grpc_url, tcp_url)
							}
						}
					}
				} else {
					log.Info("获取地址失败")
					time.Sleep(time.Second * 2)
				}
			}
		}
	}()
	//阻塞直至有信号传入
	kill := <-c
	this.exit = true
	ticker.Stop()
	log.Warn("收到停止消息，开始执行退出逻辑%v", kill)
	this.Reset()
	//this.waitGroup.Wait()
	this.funcExit()
}

func (this *Room) funcAccept() {
	defer log.Warn("逻辑处理退出")
	log.Info("逻辑启动成功")
	for {
		select {
		case acceptMsg := <-this.acceptMsg:
			resp := acceptMsg.s2cNoticeMsg
			log.Debug("开始处理消息%v", resp)
			//匹配消息
			if resp.Cmd == int32(msg.CMDKIND_MATCH) {
				if this.exit {
					break
				}
				//处理一条匹配消息
				log.Debug("处理匹配消息")
				match := new(msg.S2CCmdMatch)
				err := proto.Unmarshal(resp.Body, match)
				if err != nil {
					log.Error(err.Error())
					return
				}
				log.Listen(this.match.Start, func(f interface{}) {
					f.(func(match *MatchMsg))(&MatchMsg{client: acceptMsg.client, match: match})
				}, fmt.Sprintf("处理用户匹配 %d", match.UserId))
			}
			//离开消息
			if resp.Cmd == int32(msg.CMDKIND_EXIT) {
				log.Listen(this.funcSysOut, func(f interface{}) {
					f.(func(int64))(resp.UserId)
				}, fmt.Sprintf("处理用户离开 %d", resp.UserId))
			}
			//机器人控制消息
			if resp.Cmd == int32(msg.CMDKIND_ROBOTCTL) {
				log.Listen(this.funcRobotControl, func(f interface{}) {
					robotCtl := &msg.S2CRobotConfig{}
					err := proto.Unmarshal(resp.Body, robotCtl)
					if err != nil {
						log.Warn(err.Error())
						return
					}
					f.(func(*msg.S2CRobotConfig))(robotCtl)
				}, fmt.Sprintf("机器人控制消息"))
			}
			//用户点控消息
			if resp.Cmd == int32(msg.CMDKIND_POINTCTL) {
				log.Listen(this.funcUserControl, func(f interface{}) {
					f.(func([]byte))(resp.Body)
				}, fmt.Sprintf("处理机器人控制"))
			}
			//血池推送
			if resp.Cmd == int32(msg.CMDKIND_ROOMCTL) {
				log.Info("处理血池推送")
				log.Listen(this.funcRoomControl, func(f interface{}) {
					f.(func([]byte))(resp.Body)
				}, fmt.Sprintf("处理血池推送"))
			}
			//关闭放假
			if resp.Cmd == int32(msg.CMDKIND_ROOMCLOSE) {
				//对战场可以不处理关闭消息
				if global.G_Config.GameType != 2 {
					log.Warn("收到关闭房间%s", string(resp.Body))
					str := strings.Split(string(resp.Body), ",")
					if len(str) == 2 {
						p64, _ := strconv.ParseInt(str[0], 10, 64)
						r64, _ := strconv.ParseInt(str[1], 10, 64)
						log.Listen(this.funcCloseRoom, func(f interface{}) {
							f.(func(int64, int64))(p64, r64)
						}, "")
					} else {
						log.Error("关闭房间消息错误%s", string(resp.Body))
					}
				}

			}
			//跑马灯配置
			if resp.Cmd == int32(msg.CMDKIND_MARQUEECONFIG) {
				s2cMarqueeConfigs := &msg.S2CMarqueeConfigs{}
				err := proto.Unmarshal(resp.Body, s2cMarqueeConfigs)
				if err != nil {
					log.Warn(err.Error())
					continue
				}
				//删除跑马灯
				this.marqueeConfig.Range(func(k, v interface{}) bool {
					this.marqueeConfig.Delete(k)
					return true
				})
				log.Info("收到跑马灯配置:%v", s2cMarqueeConfigs)
				for key, val := range s2cMarqueeConfigs.MarqueeConfigs {
					this.marqueeConfig.Store(fmt.Sprintf("%d", val.RuleId), s2cMarqueeConfigs.MarqueeConfigs[key])
				}
			}

			//逻辑消息
			if resp.Cmd == int32(msg.CMDKIND_LOGIC) {
				if resp.Body == nil {
					continue
				}
				frameMsg := &msg.FrameMsg{}
				err := proto.Unmarshal(resp.Body, frameMsg)
				if err != nil {
					log.Warn(err.Error())
					continue
				}
				log.Debug("%d逻辑消息%d:%d", resp.UserId, frameMsg.MainCmd, frameMsg.SubCmd)
				log.Listen(this.Accept, func(f interface{}) {
					f.(func(userId int64, mainCmd, subCmd int32, buffer []byte))(resp.UserId, frameMsg.MainCmd, frameMsg.SubCmd, frameMsg.Buff)
				}, "")
			}
		case <-this.acceptClose:
			return
		}
	}
}

func (this *Room) funcExit() {
	os.Exit(-1)
}

//机器人消息
func (this *Room) funcRobotMsg(body *AcceptMsg) {
	defer log.Trace()()
	this.acceptMsg <- body
}

//给大厅广播消息--rpc
func (this *Room) funcBroadcast(platformId, roomId int64, body []byte) error {
	defer log.Trace()()
	broadcastRequest := &msg.BroadcastRequest{
		PlatformId: platformId,
		RoomId:     roomId,
		Body:       body,
	}
	var client *Client
	go func() {
		this.conns.Range(func(key, vli interface{}) bool {
			client = vli.(*Client)
			_, err := client.grpc.Broadcast(context.Background(), broadcastRequest)
			if err != nil {
				log.Error("%s %s", err.Error(), client.id)
				return true
			}
			//log.Debug("广播消息%d", roomId)
			return true
		})
	}()
	return nil
}

//奖金池--rpc
func (this *Room) funcGetCollect(ctx context.Context, roomId int64, ration int32, userId int64, matchId string) (*msg.ColectHanselRsp, error) {
	defer log.Trace()()
	colectHanselReq := &msg.ColectHanselReq{
		Roomid:  roomId,
		Ration:  ration,
		UserId:  userId,
		MatchId: matchId,
	}
	var client *Client
	this.conns.Range(func(key, vli interface{}) bool {
		client = vli.(*Client)
		return false
	})
	if client != nil {
		resp, err := client.grpc.Collect(ctx, colectHanselReq)
		if err != nil {
			log.Error(err.Error())
			return nil, err
		} else {
			return resp, nil
		}
	} else {
		return nil, errors.New("no rpc server")
	}

}

/*
*获取机器人 -- rpc
 */
func (this *Room) funcGetRobot(platformId, roomId, tableId int64, num int32) error {
	defer log.Trace()()
	_, ok := this.robotCtl.Load(fmt.Sprintf("%d-%d", platformId, roomId))
	if ok {
		//存在直接返回
		log.Warn("此房间禁止机器人加入%d-%d", platformId, roomId)
		return nil
	}
	getRobotRequest := &msg.GetRobotRequest{
		PlatformId: platformId,
		RoomId:     roomId,
		TableId:    tableId,
		Token:      this.token,
		Num:        num,
	}

	var client *Client
	this.conns.Range(func(key, vli interface{}) bool {
		client = vli.(*Client)
		return false
	})

	if client != nil {
		go func() {
			_, err := client.grpc.GetRobot(this.ctx, getRobotRequest)
			if err != nil {
				log.Error(err.Error())
				return
			}
			log.Debug("获取机器人成功%d-%d", platformId, roomId)
		}()
	} else {
		log.Error("获取机器人失败 没rpx sever%d-%d", platformId, roomId)
	}
	return nil
}

//发送日志
func (this *Room) funcWrietLogs(cmd int32, userId int64, body []byte) error {
	defer log.Trace()()

	var client *Client
	this.conns.Range(func(key, vli interface{}) bool {
		client = vli.(*Client)
		return false
	})

	if client != nil {
		sendMsg := &msg.C2SNoticeMsg{Cmd: cmd, UserId: userId, Body: body, Token: this.token, MsgId: time.Now().UnixNano()}
		client.funcWrite(sendMsg)
		log.Info("发送日志成功")
	} else {
		log.Error("发送日志失败 没服务")
	}
	return nil
}

//发送战绩
func (this *Room) funcSendMsg(cmd int32, userId int64, body []byte) error {
	defer log.Trace()()

	var client *Client
	this.conns.Range(func(key, vli interface{}) bool {
		client = vli.(*Client)
		return false
	})

	if client != nil {
		sendMsg := &msg.C2SNoticeMsg{Cmd: cmd, UserId: userId, Body: body, Token: this.token, MsgId: time.Now().UnixNano()}
		client.funcWrite(sendMsg)
	} else {
		log.Error("发送战绩失败没找到服务")
	}
	return nil
}

/*
*归还机器人
 */
func (this *Room) funcReturnRobot(userId int64) {
	defer log.Trace()()
	this.aiManager.DelRobot(userId)
}

/*
*网络断开或者系统退出
 */
func (this *Room) funcSysOut(userId int64) {
	defer log.Trace()()
	log.Info("收到用户异常离开消息%d", userId)
	user := this.userManager.GetById(userId)
	if user != nil {
		//判断此用户在桌子内还是房间内
		tableId := user.GetTableId()
		if tableId >= 0 {
			table := this.tableManager.GetTableById(tableId)
			table.UserExit(user)
		} else {
			this.interRoom.UserExit(user)
			this.userManager.DeleteById(user.id)
		}
	}
}

/*
*雪池控制
 */
func (this *Room) funcRoomControl(body []byte) {
	defer log.Trace()()
	roomCtl := &msg.S2CRoomCtl{}
	err := proto.Unmarshal(body, roomCtl)
	if err != nil {
		log.Warn(err.Error())
		return
	}
	log.Info("收到更新血池%v", roomCtl)
	this.tableManager.Traverse(func(key int, table *Table) bool {
		if table.roomID == roomCtl.Roomid {
			log.Info("更新血池%d,%d", table.roomID, roomCtl.Prob)
			table.SetRoomProb(roomCtl.Prob)
		}
		return true
	})
}

/*
*用户控制
 */
func (this *Room) funcUserControl(body []byte) {
	defer log.Trace()()
	pointCtl := &msg.S2CPointCtl{}
	err := proto.Unmarshal(body, pointCtl)
	if err != nil {
		log.Warn(err.Error())
		return
	}
	user := this.userManager.GetById(pointCtl.Userid)
	if user != nil {
		log.UserInfoLog(pointCtl.Userid, "收到用户点控 prob:%d,roomId:%d", pointCtl.Prob, user.roomId)
		user.SetProb(pointCtl.Prob)
	}
}

/*
*机器人控制
 */
func (this *Room) funcRobotControl(robotCtl *msg.S2CRobotConfig) {
	defer log.Trace()()

	if robotCtl.Act == msg.S2CRobotConfig_ON {
		log.Debug("开启机器人%d-%d", robotCtl.PlatformId, robotCtl.RoomId)
		this.robotCtl.Delete(fmt.Sprintf("%d-%d", robotCtl.PlatformId, robotCtl.RoomId))
	}
	if robotCtl.Act == msg.S2CRobotConfig_OFF {
		//所有房间机器人退出
		this.aiManager.Traverse(func(value *User) bool {
			if value.GetRoomID() == robotCtl.RoomId && value.platformId == robotCtl.PlatformId {
				this.funcSysOut(value.GetId())
			}
			return true
		})
		log.Debug("关闭机器人%d-%d", robotCtl.PlatformId, robotCtl.RoomId)
		this.robotCtl.Store(fmt.Sprintf("%d-%d", robotCtl.PlatformId, robotCtl.RoomId), robotCtl.Act)
	}
}

/*
*用户准备
 */
func (this *Room) funcReady(userId int64, buff []byte) {
	defer log.Trace()()
	user := this.userManager.GetById(userId)
	if user == nil {
		log.Error("用户不存在 %d", userId)
		return
	}
	tableId := user.GetTableId()
	if tableId > -1 {
		table := this.tableManager.GetTableById(tableId)
		table.UserReady(user)
	} else {
		log.Warn("用户没有坐下 暂时不处理")
	}
}

/*
*用户离开
 */
func (this *Room) funcLeave(userId int64) {
	defer log.Trace()()
	user := this.userManager.GetById(userId)
	if user == nil {
		return
	}
	tableId := user.GetTableId()
	if tableId > -1 {
		table := this.tableManager.GetTableById(tableId)
		table.UserLeave(user)
	} else {
		log.Warn("用离开没有坐下 暂时不处理")
	}
}

/*
*接受连接
 */
func (this *Room) Accept(userId int64, mainCmd, subCmd int32, buffer []byte) {
	defer log.Trace()()

	if mainCmd == int32(msg.MSGTYPE_MAINCMD) {
		if subCmd == int32(msg.MSGKIND_READY) {
			defer recover_handle.RecoverHandle("准备")
			//用户准备
			this.funcReady(userId, buffer)
		}
		if subCmd == int32(msg.MSGKIND_LEAVE) {
			//用户离开
			this.funcLeave(userId)
		}
	}
	//逻辑消息
	if mainCmd == int32(msg.MSGTYPE_SUBCMD) {
		user := this.userManager.GetById(userId)
		if user != nil {
			//查找用户所在桌子
			tableId := user.GetTableId()
			if tableId > -1 {
				table := this.tableManager.GetTableById(tableId)

				table.GameMessage(&MsgTableChan{SubCmd: subCmd, Buffer: buffer, User: user})
			} else {
				//没有坐下的用户不处理
				log.Warn("此用户为没有坐下用户")
			}
		}
	}
}

/*
*退出处理
 */
func (this *Room) Reset() {
	this.tableManager.Traverse(func(key int, value *Table) bool {
		value.Reset()
		return true
	})
	this.waitUserGroup.Wait()
	log.Info("重置完成!!!!!!")
}

//关闭指定房间
func (this *Room) funcCloseRoom(platformId, roomId int64) {
	//log.Info("开始关闭房间")
	this.tableManager.Traverse(func(key int, value *Table) bool {
		if value.roomID == roomId && value.platformId == platformId {
			log.Info("关闭这个房间%d-%d", roomId, platformId)
			value.Reset()
		} else {
			//log.Info("不是需要关闭这个房间%d-%d %d-%d", roomId, platformId, value.roomID, value.platformId)
		}
		return true
	})
	//log.Info("关闭结束")
}
